# -*- coding:utf-8 -*-
#
# Copyright © 2014 Lucian Smith
# Based on the 'pylint' plugin, and the spyderlib/plugins/editor core plugin.
# Licensed under the terms of the MIT License
# (see spyderlib/__init__.py for details)

"""Export from Python to Combine Archive Plugin"""
import os
import re
import zipfile
import spyderplugins.widgets.pyToSedml as py
import tellurium as te
import tempfile, shutil, errno
from string import Template
from xml.etree import ElementTree
from spyderlib.baseconfig import get_translation
from spyderlib.config import CONF, get_filter
from spyderlib.plugins import SpyderPluginMixin, PluginConfigPage
from spyderlib.py3compat import getcwd, to_text_string, is_text_string
from spyderlib.qt.QtCore import SIGNAL
from spyderlib.widgets.tabs import BaseTabs
from spyderlib.qt.QtGui import QVBoxLayout, QGroupBox, QWidget, QApplication, QMessageBox, QMenu
from spyderlib.qt.compat import getopenfilenames, getsavefilename
from spyderlib.utils import encoding, sourcecode
from spyderlib.utils.qthelpers import get_icon, create_action, add_actions
from spyderlib.widgets.sourcecode.codeeditor import CodeEditor

_ = get_translation("p_export2combine", dirname="spyderplugins")

class P2CConfigPage(PluginConfigPage):
    def setup_page(self):
        settings_group = QGroupBox(_("Settings"))
        save_box = self.create_checkbox(_("Placeholder in case we at some point need a settings page."),
                                        'tick_value', default=True)
        
        settings_layout = QVBoxLayout()
        settings_layout.addWidget(save_box)
        settings_group.setLayout(settings_layout)

        vlayout = QVBoxLayout()
        vlayout.addWidget(settings_group)
        vlayout.addStretch(1)
        self.setLayout(vlayout)


class P2C(QWidget, SpyderPluginMixin):
    """Export to combine archive from a python script"""
    CONF_SECTION = 'p2c'
    CONFIGWIDGET_CLASS = P2CConfigPage    
    def __init__(self, parent=None):
        QWidget.__init__(self, parent=parent)
        SpyderPluginMixin.__init__(self, parent)
        layout = QVBoxLayout()
        self.setLayout(layout)
        
#        self.recent_files = self.get_option('recent_files', [])

        # Initialize plugin
        self.initialize_plugin()
        
    #------ SpyderPluginWidget API --------------------------------------------
    def get_plugin_title(self):
        """Return widget title"""
        return _("Python to combine archive exporter")
    
    def get_plugin_icon(self):
        """Return widget icon"""
        return None#get_icon('hello.png')
    
    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        return None
    
    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        # Font
        return []

    def on_first_registration(self):
        """Action to be performed on first plugin registration"""
        self.main.tabify_plugins(self.main.inspector, self)
        self.dockwidget.hide()

    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.connect(self, SIGNAL("edit_goto(QString,int,QString)"),
                     self.main.editor.load)
        self.connect(self, SIGNAL('redirect_stdio(bool)'),
                     self.main.redirect_internalshell_stdio)
        self.main.add_dockwidget(self)
        
        p2c_act = create_action(self, _("Export to combine archive"),
                                   triggered=self.run_p2c)
        p2c_act.setEnabled(True)
        self.register_shortcut(p2c_act, context="Python to combine",
                               name="Export to combine archive", default="Alt-P")
        
        p2c_actions = (None, p2c_act)
        export_menu = QMenu(_("Export"))
        add_actions(export_menu, p2c_actions)
        self.main.file_menu_actions.insert(8, export_menu)

    def refresh_plugin(self):
        """Refresh widget"""
        pass
        
    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True
            
    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        #self.hello_number = self.get_option('hello_number', 1)
        pass

    def run_p2c(self):
        """Prompt the user to load a python script and export into combine archive"""
        
#        editorwindow = None #Used in editor.load
#        processevents = True  #Used in editor.load
        editor = self.main.editor
#        basedir = getcwd()
        parent_widget = editor.get_current_editorstack()
        p_fname = editor.get_current_filename()
        basename = p_fname[:-3] + '.zip'
#        if CONF.get('workingdir', 'editor/save/auto_set_to_basedir'):#open/browse_scriptdir'):
#            if p_fname is not None and p_fname != editor.TEMPFILE_PATH:
#                basedir = os.path.dirname(p_fname)
        
        editor.emit(SIGNAL('redirect_stdio(bool)'), False)
        selectedfilter = ''
        filters = 'Combine archive (*.zip);;All files (*.*)'
        filename, _selfilter = getsavefilename(parent_widget,
                                     _("Export to combine archive"), basename, filters,
                                     selectedfilter=selectedfilter)
        editor.emit(SIGNAL('redirect_stdio(bool)'), True)
        
        if filename:
            if CONF.get('workingdir', 'editor/save/auto_set_to_basedir'):
                directory = os.path.dirname(p_fname)
                editor.emit(SIGNAL("file_renamed_in_data(QString,int,QString)"), directory)
        else:
            #The file dialog box was closed without selecting a file.
            return
        Export2combine(filename, p_fname)
#        focus_widget = QApplication.focusWidget()
#        if editor.dockwidget and not editor.ismaximized and\
#           (not editor.dockwidget.isAncestorOf(focus_widget)\
#            and not isinstance(focus_widget, CodeEditor)):
#            editor.dockwidget.setVisible(True)
#            editor.dockwidget.setFocus()
#            editor.dockwidget.raise_()
#        
#        def _convert(fname):
#            fname = os.path.abspath(encoding.to_unicode_from_fs(fname))
#            if os.name == 'nt' and len(fname) >= 2 and fname[1] == ':':
#                fname = fname[0].upper()+fname[1:]
#            return fname

#        if hasattr(filenames, 'replaceInStrings'):
#            # This is a QStringList instance (PyQt API #1), converting to list:
#            filenames = list(filenames)
#        if not isinstance(filenames, list):
#            filenames = [_convert(filenames)]
#        else:
#            filenames = [_convert(fname) for fname in list(filenames)]
        
        
#        p = re.compile( '(.zip$)')
#        pythonfile = p.sub( '.py', filename)
#        if (pythonfile == filename):
#            pythonfile = filename + ".py"
#        current_editor = editor.set_current_filename(pythonfile, editorwindow)
#        if current_editor is not None:
#            # -- TODO:  Do not open an already opened file
#            pass
#        else:
#            # -- Not an existing opened file:
#            if not os.path.isfile(filename):
#                continue
#            # --
#            current_es = editor.get_current_editorstack(editorwindow)
#            # Creating the editor widget in the first editorstack (the one
#            # that can't be destroyed), then cloning this editor widget in
#            # all other editorstacks:
#            finfo, newname = self.load_and_translate(filename, pythonfile, editor)
#            finfo.path = editor.main.get_spyder_pythonpath()
#            editor._clone_file_everywhere(finfo)
#            current_editor = current_es.set_current_filename(newname)
#            if (current_editor is not None):
#                editor.register_widget_shortcuts("Editor", current_editor)
#            
#            current_es.analyze_script()
#        if (current_editor is not None):
#            current_editor.clearFocus()
#            current_editor.setFocus()
#            current_editor.window().raise_()
#        if processevents:
#            QApplication.processEvents()

#    def load_and_translate(self, pyscript, pythonfile, editor, set_current=True):
#        """
#        Read filename as combine archive, unzip, translate, reconstitute in 
#        Python, and create an editor instance and return it
#        *Warning* This is loading file, creating editor but not executing
#        the source code analysis -- the analysis must be done by the editor
#        plugin (in case multiple editorstack instances are handled)
#        """
#        pyscript = str(pyscript)
#        self.emit(SIGNAL('starting_long_process(QString)'),
#                  _("Loading %s...") % pyscript)
#        text, enc = encoding.read(pyscript)
#        text = Export2combine(pyscript)
#        widgeteditor = editor.editorstacks[0]
#        finfo = widgeteditor.create_new_editor(pythonfile, enc, text, set_current, new=True)
#        index = widgeteditor.data.index(finfo)
#        widgeteditor._refresh_outlineexplorer(index, update=True)
#        self.emit(SIGNAL('ending_long_process(QString)'), "")
#        if widgeteditor.isVisible() and widgeteditor.checkeolchars_enabled \
#         and sourcecode.has_mixed_eol_chars(text):
#            name = os.path.basename(pythonfile)
#            QMessageBox.warning(self, widgeteditor.title,
#                                _("<b>%s</b> contains mixed end-of-line "
#                                  "characters.<br>Spyder will fix this "
#                                  "automatically.") % name,
#                                QMessageBox.Ok)
#            widgeteditor.set_os_eol_chars(index)
#        widgeteditor.is_analysis_done = False
#        return finfo, pyscript

def Export2combine(zipname, p_fname):
    
    def search(line, string):
        for i,s in enumerate(line):
            if string in s:
                return i
                    
    #Collects and creates an archive
    def Zipit(zipname, p_fname):
        #sbmlname = GetModelName(p_fname) + ".xml"
        #sedmlname = GetModelName(p_fname) + "_sedml.xml"
        
        sbmlstr_t, sedmlstr_t, sbmlname, sedmlname = translate(p_fname)
        manifeststr = manifestconstruct(sbmlname, sedmlname)
        tarzip = zipfile.ZipFile(zipname, mode='w')
        tarzip.writestr("manifest.xml", manifeststr)
        tarzip.writestr(sbmlname, sbmlstr_t)
        tarzip.writestr(sedmlname, sedmlstr_t)
        tarzip.close()
    
    #Extracts model name from python string
    def GetModelName(pyloc):
        lines = te.readFromFile(pyloc)
        line = lines.splitlines()
        for i in range(0, len(line)):
            if "//Created by libAntimony" in line[i]:
                index = i + 1
        modelname = line[index][6:-2]
        
        return modelname
    
    #Constructs a manifest string
    def manifestconstruct(sbmlname, sedmlname):
        manifesttemplate = Template('''<?xml version="1.0"  encoding="utf-8"?>
<omexManifest  xmlns="http://identifiers.org/combine.specifications/omex-manifest">
    <content location="./manifest.xml" format="http://identifiers.org/combine.specifications/omex-manifest"/>
    <content location="$sbmlloc" format="http://identifiers.org/combine.specifications/sbml"/>
    <content location="$sedmlloc" master="true" format="http://identifiers.org/combine.specifications/sed-ml"/>
</omexManifest>''')
        manifeststr = manifesttemplate.substitute(sbmlloc = str("./" + sbmlname), sedmlloc = str("./" + sedmlname))
        
        #print manifeststr
        return manifeststr

    #Search and translate SBML and SEDML strings in python script
    def translate(pyloc):
        #py.seek(0)
        temp = []
        reCombineModelPath = r"(\w*) = te.(loada|loadAntimonyModel)\((.*)\)"
        lines = te.readFromFile(pyloc)
        line = lines.splitlines()
        if search(line, "//Created by libAntimony") is not None:
            if search(line, "end") is not None:
                sbmlindex_i = search(line, "//Created by libAntimony")
                sbmlindex_f = search(line, "end")
        else:
            raise Exception("Error: Python script does not contain correctly formatted SBML string.")
        #if search(line, "# Beginning of generated script") is not None:
        #    if search(line, "# End of generated script") is not None:
        #        sedmlindex_i = search(line, "# Beginning of generated script")
        #        sedmlindex_f = search(line, "# End of generated script")
        #else:
        #    raise Exception("Error: Python script does not contain necessary details to reconstruct SEDML string.")
        for i,s in enumerate(line):
            reCombinePath = re.split(reCombineModelPath, s)
            if len(reCombinePath) > 1:
                temp.append(reCombinePath[3])
        sbmlname = str(temp[0]) + '.xml'
        sedmlname = str(temp[0]) + '_sedml.xml'
        sbmlstr = '\n'.join(line[sbmlindex_i:sbmlindex_f + 1])
        sedmlstr = lines#'\n'.join(line[sedmlindex_i:sedmlindex_f + 1])
        #print sbmlstr
        #print sedmlstr
        sbmlstr_t = te.antimonyTosbml(sbmlstr)
        sedmlstr_t = py.string_to_sedml(sedmlstr)
        
        return sbmlstr_t, sedmlstr_t, sbmlname, sedmlname
    
    Zipit(zipname, p_fname)    



#==============================================================================
# The following statements are required to register this 3rd party plugin:
#==============================================================================
PLUGIN_CLASS = P2C

